<?php

!defined('ACLOUD_PATH') && exit('Forbidden');
require_once Wind::getRealPath('ACLOUD_VER:dataflow.ACloudVerDataFlowAggregate');
class ACloudSysDataFlowServiceAggregate
{
    private $service = array();

    public function __construct()
    {
        if (!isset($this->service['parse']) || !$this->service['parse']) {
            $this->service['parse'] = new Aggregate_SQLParseExtension();
        }
        if (!isset($this->service['operate']) || !$this->service['operate']) {
            $this->service['operate'] = new Aggregate_SQLLogExtension();
        }
    }

    public function collectSQL($sql, $params)
    {
        list($bool, $operate, $tableName, $fields) = $this->service['parse']->parseSQL($sql);
        if (!$bool) {
            return false;
        }

        return $this->service['operate']->operate($operate, $tableName, $sql, $fields, $params);
    }
}

class Aggregate_SQLParseExtension
{
    private $prefix = '';

    public function __construct()
    {
        $this->prefix = ACloudSysCoreCommon::getGlobal(ACloudSysCoreDefine::ACLOUD_OBJECT_DAO)->getDB()->getTablePrefix();
    }

    public function parseSQL($sql)
    {
        list($sql, $info) = array(trim($sql), array());
        if (!$sql) {
            return array(false, '', '', $info);
        }
        list($bool, $operate, $tableName) = $this->matchOperateAndTableName($sql);
        if (!$bool) {
            return array(false, '', '', $info);
        }
        if (ACloudSysCoreS::inArray($operate, array('insert', 'replace'))) {
            $dao = ACloudSysCoreCommon::getGlobal(ACloudSysCoreDefine::ACLOUD_OBJECT_DAO);
            $insertId = $dao->insert_id();
            $info = array('insertid' => $insertId);
        }

        return array(true, $operate, $tableName, $info);
    }

    private function matchOperateAndTableName($sql)
    {
        preg_match('/^(DELETE|INSERT|REPLACE)\s+(.+?\s)?`?'.$this->prefix.'(\w+)`?\s+/i', $sql, $match);
        if (!$match) {
            return array(false, false, false);
        }
        list(, $operate, , $tableName) = $match;
        list($operate, $tableName) = array(strtolower($operate), strtolower($tableName));
        if (!ACloudSysCoreS::inArray($tableName, $this->getTables())) {
            return array(false, false, false);
        }

        return array(true, $operate, $tableName);
    }

    private function getTables()
    {
        return ACloudVerDataFlowAggregate::getMonitorTables();
    }
}

class Aggregate_SQLLogExtension
{
    private $params = array();

    public function operate($operate, $tableName, $sql, $fields, $params)
    {
        return ($operate == 'delete') ? $this->operateDeleteLog($sql, $params) : $this->operateAddLog($tableName, $fields);
    }

    private function operateDeleteLog($sql, $params)
    {
        $this->setParams($params);
        $sign = ACloudSysCoreCommon::getSiteSign();
        setcookie('_ac_'.$sign, intval(ACloudVerDataFlowAggregate::getDeleteSig()), time() + 60);
        $sqlLogService = ACloudSysCoreCommon::loadSystemClass('sql.log', 'config.service');
        $fields = array('log' => $this->buildSql($sql));

        return $sqlLogService->addSqlLog($fields);
    }

    private function operateAddLog($tableName, $fields)
    {
        list($type, $insertId) = array(ACloudVerDataFlowAggregate::getTypeByTableName($tableName), $fields['insertid']);
        if (is_null($type) || !$insertId) {
            return false;
        }
        $sign = ACloudSysCoreCommon::getSiteSign();

        return setcookie('_ac_'.$sign, intval($type), time() + 3600);
    }

    private function buildSql($sql)
    {
        if (!ACloudSysCoreS::isArray($this->getParams())) {
            return $sql;
        }

        return preg_replace_callback('/\?/', array($this, 'replaceSqlParams'), $sql);
    }

    private function replaceSqlParams($matches)
    {
        $params = $this->getParams();
        $replace = array_shift($params);
        $this->setParams($params);

        return $replace;
    }

    private function setParams($params)
    {
        $this->params = $params;
    }

    private function getParams()
    {
        return $this->params;
    }
}
